/*
* 1. 完整一次通讯总是由cat1主动发起，且必须等到M0回应。
* 2. 完整一次通讯周期不得>100ms
* 3. 下次命令必须间隔>=50ms
*/

#include "zq_gl.h"
#include "ql_api_dev.h"
#include "ql_api_nw.h"
#include "ql_api_sim.h"
#include "ql_uart.h"

#include "zq_app.h"
#include "dtu.h"
// #include "gl_variable.h"

ql_task_t zq_dtu_task;
ql_mutex_t DtuMutex = NULL;
static ql_sem_t dtu_sem = NULL;

// uart send/receive related low layer function  -----------------------------------------------------------------------------------------------
static unsigned char recv_buf[ZQ_RX_MAX_SIZE];
static short recv_len = 0;
// 这个函数被系统回调, 不做耗时处理, 只简单copy和拼包.
static void ql_uart_notify_cb(unsigned int ind_type, ql_uart_port_number_e port, unsigned int size)
{
    if (size <= 0) return;
    switch(ind_type)
    {
        case QUEC_UART_RX_OVERFLOW_IND:  //rx buffer overflow
        case QUEC_UART_RX_RECV_DATA_IND:
			//ZQ_ASSERT((recv_len + size) <= ZQ_RX_MAX_SIZE, "dtu received data over size !");
			if((recv_len + size) > ZQ_RX_MAX_SIZE)  // 数据异常, 抛弃
				return;
			recv_len += ql_uart_read(port, recv_buf + recv_len, size);
            break;
        case QUEC_UART_TX_FIFO_COMPLETE_IND: 
            //ZQLOG("uart_log: tx fifo complete");
            break;
    }
}
static int DtuInitUart(void)
{
    int ret = 0;
    ql_uart_config_s uart_cfg = {0};
    /***********************************************************
	Note start:
        1.If the BAUD rate is QL UART BAUD_AUTO,a string of 'at'should be sent at least once to identify the baud rate.
        2.Once the baud rate is identified, it cannot be changed unless restarted.
    ************************************************************/
	uart_cfg.baudrate = QL_UART_BAUD_230400; // 两个CPU之间有电平转换电路，不建议超过230400.
    uart_cfg.flow_ctrl = QL_FC_NONE;
    uart_cfg.data_bit  = QL_UART_DATABIT_8;
    uart_cfg.stop_bit  = QL_UART_STOP_1;
    uart_cfg.parity_bit = QL_UART_PARITY_NONE;

    ret = ql_uart_set_dcbconfig(QL_CUR_UART_PORT, &uart_cfg);
    if(ret != ZQ_RET_OK){
        ZQ_LOG_FAILED(ql_uart_set_dcbconfig, ret);
        return ret;
    }
    ret = ql_uart_open(QL_CUR_UART_PORT);
    if(ret != ZQ_RET_OK) {
        ZQ_LOG_FAILED(ql_uart_open, ret);
        return ret;
    }
    ret = ql_uart_register_cb(QL_CUR_UART_PORT, ql_uart_notify_cb);
    if(ret != ZQ_RET_OK) {
        ZQ_LOG_FAILED(ql_uart_register_cb, ret);
        return ret;
    }
    
    return ZQ_RET_OK;
}
// uart send/receive related function  -----------------------------------------------------------------------------------------------
static ZqCommPkg * dtu_recv(uint8_t *data, uint16_t* plen)
{
	// receive response data; 时长控制在400ms以内; pkt == NULL 表示收包不完整,继续收包拼包
	for (int j = 0; j < 20; j++, ql_rtos_task_sleep_ms(20)) {
		if (*plen < sizeof(ZqCommPkg)) continue; // wait more data
		ZqCommPkg* pkg = (ZqCommPkg*)data;
		if (pkg->flag != COMM_PKG_HEAD_FLAG || pkg->len > ZQ_RX_MAX_SIZE || pkg->len < sizeof(ZqCommPkg)) // 标志出错或长度出错, reset
			break;
		if (pkg->len > *plen) continue; // wait more data
		if (ZqGetChecksum(data, pkg->len) != 0) // checksum error
			break;
		*plen = 0;
		return pkg;
	}
	*plen = 0;
	return NULL;
}
static int dtu_send(uint8_t cmd, uint8_t *buf, uint16_t len)
{
	ZqCommPkg* pkg = (ZqCommPkg*)buf;
	pkg->flag = COMM_PKG_HEAD_FLAG;
	pkg->len = len;
	pkg->cmd = cmd;
	pkg->checksum = 0;
	pkg->checksum = ZqGetChecksum(buf, pkg->len);
	ql_uart_write(QL_CUR_UART_PORT, buf, pkg->len);
	return len;
}
// uart package/unpackage  function  -----------------------------------------------------------------------------------------------

// this function send general dtu command, note: the max block time is about 100ms
// paraams: cmd, send buffer and data size; receive buffer and buffer size.
// return: < 0: error code; >=0 : success, and the receive data size 
static int dtu_cmd(uint8_t cmd, uint8_t *sbuf, short ssize, uint8_t *rbuf, short rsize)
{
	ZqCommPkg *pkg = NULL;
	ql_rtos_semaphore_wait(dtu_sem, QL_WAIT_FOREVER);
	// for(int i = 0; i < 3 && ppkg == NULL; i++) //	// 不用重试，因为第一次不成功后，也许后面的重试命令都不对。比如读取门开关次数, 也许第一次失败的时候就执行,数据已经清掉. 后面重试也得不到数据了.
	{
		recv_len = 0; // before send cmd, clear the receive buf, get rid of last trans data interference
		dtu_send((uint16_t)cmd, (uint8_t *)sbuf, (uint16_t)ssize); // send cmd data
		pkg = dtu_recv(recv_buf, (uint16_t *)&recv_len);
	}
	// error handing and copy data
	int ret = -1;
	if(pkg == NULL) {
		ZQLOG("@err@: dtu_cmd command over time !");
		ret = ZQ_RET_OVERTIME_ERR;
	} 
	else if(cmd != pkg->cmd) {
		ZQLOG("@err@: dtu_cmd received data does not match command !");
		ret = ZQ_RET_ERR;
	}
	else if (rsize < (pkg->len - sizeof(ZqCommPkg)) || pkg->len <= 0) {
		ZQLOG("@err@: dtu_cmd received data buffer size is not enough !");
		ret = ZQ_RET_ERR;
	}
	else {
		ret = pkg->len - sizeof(ZqCommPkg);
		memcpy(rbuf, pkg->data, ret);
	}
	// overtime = 0;
	ql_rtos_semaphore_release(dtu_sem);
	return ret;
}
// 命令1 - 交换基础信息，下发cat1版本， 查询M0版本, ble mac等信息
int dtu_cmd01(DtuC01ResponsePkg *pInfo)
{
	int s_len = sizeof(ZqCommPkg);
	uint8_t buf[128];
	for (int i = 0; i < 3; i++, ql_rtos_task_sleep_ms(1000)) { // retry 3 times
		DtuC01RequestPkg* req = (DtuC01RequestPkg*)(buf + s_len);
		req->com_protocol_ver = COMM_PROTOCOL_VER;
		strcpy(req->cat1_sw_ver, ZQ_SW_VER);
		s_len += sizeof(DtuC01RequestPkg);
		if (dtu_cmd(COMM_CMD_BASE, buf, s_len, buf, 128) > 0) {
			memcpy(pInfo, buf, sizeof(DtuC01ResponsePkg));
			return ZQ_RET_OK;
		}
	}
	return ZQ_RET_ERR;
}
// 命令2 - 1. 发送网络状态信息, 2. 查询所有温控信息, 3. 保持心跳
int dtu_cmd02(DtuC02RequestPkg *req, DtuC02ResponsePkg *resp)
{
	int ret;
	int s_len = sizeof(ZqCommPkg);
	uint8_t buf[sizeof(ZqCommPkg) + sizeof(DtuC02ResponsePkg)];
	uint8_t *p = buf + s_len;
	memcpy(p, req, sizeof(DtuC02RequestPkg));
	s_len += sizeof(DtuC02RequestPkg);
	ret = dtu_cmd(COMM_CMD_EXCH, buf, s_len, buf, sizeof(buf));
	if(ret < 0){		
		ZQLOG("dtu_cmd02 error: %d", ret);
		return ZQ_RET_ERR;
	}
	//ZQLOG("dtu cmd02 receive response: len:%d, and sizeof(devst):%d", ret, sizeof(IotCmd02RespPkg));
	// Modified by Conan -- 2023-04-06 11:18 -- 兼容910修改，mcu上传IotCmd02RespPkg增加了一个字节tempControlMode
	if(ret != sizeof(DtuC02ResponsePkg)){
		ZQLOG("@err@: receive data length error: %d", ret);
		return ZQ_RET_ERR;
	}
	memcpy(resp, buf, sizeof(DtuC02ResponsePkg));
	return ZQ_RET_OK;
}
// 命令3 - 设置controllor parameters
int dtu_cmd03(DtuC03ReqRespPkg *pDev)
{
	int ret;
	uint8_t buf[sizeof(ZqCommPkg) + sizeof(DtuC03ReqRespPkg)];
	uint8_t *p = buf + sizeof(ZqCommPkg);
	memcpy(p, pDev, sizeof(DtuC03ReqRespPkg));
	ret = dtu_cmd(COMM_CMD_SETUP, buf, sizeof(buf), (uint8_t*)pDev, sizeof(buf));
	if(ret < 0){		
		ZQLOG("dtu_cmd03 error: %d", ret);
		return ZQ_RET_ERR;
	}
	return ZQ_RET_OK;
}

// 命令0x10 - erase mcu flash
// CAT1 -- > mcu  : cmd10 -- flash_erase( 0x12345678 4B) 
// mcu -- > CAT1  : cmd10 -- ack(1B) 
int dtu_cmd10(void)
{
	int ret;
	int s_len = sizeof(ZqCommPkg);
	uint8_t buf[32];
	uint8_t *p = buf + s_len;
	p[0] = 0x12;
	p[1] = 0x34;
	p[2] = 0x56;
	p[3] = 0x78;
	s_len += 4;
	ret = dtu_cmd(COMM_CMD_OTA_START, buf, s_len, buf, 128);
	if(ret < 0){		
		ZQLOG("dtu_cmd10 error: %d", ret);
		return ZQ_RET_ERR;
	}
	return ZQ_RET_OK;
}
// 命令0x11 - write data to mcu flash
// CAT1 -- > mcu  : cmd11 -- flash_write( addr:4B, size:2B, data:nB ) 
// mcu -- > CAT1  : cmd11 -- ack(1B) 
int dtu_cmd11(unsigned int offset, char *data, short size)
{
	int ret;
	uint8_t buf[ZQ_RX_MAX_SIZE];
	int s_len = sizeof(ZqCommPkg);
	uint8_t *p = buf + s_len;
	if(size <= 0 || size > 512){
		ZQLOG("data size error: %d", size);
		return ZQ_RET_ERR;
	}
	p[0] = (offset>>24)&0xff;
	p[1] = (offset>>16)&0xff;
	p[2] = (offset>>8)&0xff;
	p[3] = (offset>>0)&0xff;
	p[4] = (uint8_t)((size >> 8)& 0xff);
    p[5] = (uint8_t)(size & 0xff);
    p[6] = 0; // 尽量保持4字节对齐
    p[7] = 0;
	s_len += 8;
	memcpy(&p[8], data, size);
	s_len += size;
	ret = dtu_cmd(COMM_CMD_OTA_DATA, buf, s_len, buf, 128);
	if(ret < 0){		
		ZQLOG("dtu_cmd11 error: %d", ret);
		return ZQ_RET_ERR;
	}
	return ZQ_RET_OK;
}
// 命令0x12 - verify mcu flash
// CAT1 -- > mcu  : cmd12 -- flash_verify( sum:1B )
// mcu -- > CAT1  : cmd12 -- ack(1B) 
int dtu_cmd12(char *data, int len)
{
	int ret;
	uint8_t buf[16];
	int s_len = sizeof(ZqCommPkg);
	uint8_t *p = buf + s_len;
	p[0] = ZqGetChecksum((unsigned char *)data, len);
	s_len += 1;
	ret = dtu_cmd(COMM_CMD_OTA_END, buf, s_len, buf, 16);
	if(ret < 0){		
		ZQLOG("dtu_cmd12 error: %d", ret);
		return ZQ_RET_ERR;
	}
	return ZQ_RET_OK;
}
//// 命令20 - 通知SIM卡信息 - 目前测试命令
////ver 16B + IMEI 16B +IMSI 16B + ICCID 32B + rssi 16B + signalStrength 1B + suffix(3B 补全到4字节对齐)
//// extern int zq_GetSignalIntensity(void);
//extern int GetSignalIntensity(void);
//int dtu_cmd20(const char *pimei)
//{
//	int ret;
//	int size = 0;
//	char buf[128];
//
//	memset(buf, 0, 100);
//	//strcpy( (char*)buff, SOFTWARE_VERSION );	//SOFTWARE_VERSION
//	strcpy(buf, AKM_CAT1_SW_VER);
//	memcpy(&buf[16], pimei, 16);
//	// ql_dev_get_imei(&buf[16], 16, ZQ_SIM_0);
//	ql_sim_get_imsi(ZQ_SIM_0, (char *)&buf[32], 16);
//	ql_sim_get_iccid(ZQ_SIM_0, (char *)&buf[48], 32);
//	int signal = GetSignalIntensity();
//	sprintf((char *)&buf[80], "%d", signal);
//	ql_nw_get_csq(ZQ_SIM_0, (unsigned char *)&buf[96]);
//	size = 100; // 3B 补全到4字节对齐
//	ZQLOG("ver:%s, IMEI:%s, rssi:%s, signal:%d", buf, &buf[16], &buf[80], buf[96]);
//	ret = dtu_cmd(0x20, buf, size, buf, 128);
//	if(ret < 0){		
//		ZQLOG("dtu_cmd20 error: %d", ret);
//		return ZQ_RET_ERR;
//	}
//	return ZQ_RET_OK;
//}
// 命令0x75, 通知cat1可以休眠
int dtu_cmd75(void)
{
	int ret;
	int s_len = sizeof(ZqCommPkg);
	uint8_t buf[sizeof(ZqCommPkg)];
	ret = dtu_cmd(COMM_CMD_SLEEP, buf, s_len, buf, s_len);
	if (ret < 0) {
		ZQLOG("dtu_cmd75 error: %d", ret);
		return ZQ_RET_ERR;
	}
	return ZQ_RET_OK;
}

void zq_dtu_start( void )
{
	int ret = 0;
	ret |= ql_rtos_mutex_create(&DtuMutex);
	ret |= ql_rtos_semaphore_create(&dtu_sem, 1);
	ret |= DtuInitUart();
	ZQ_ASSERT((ret == ZQ_RET_OK), "DTU Init fail!!");
}
